package com.runtu.config;

import com.runtu.types.DCCValue;
import com.runtu.types.common.Constants;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.redisson.Redisson;
import org.redisson.api.RBucket;
import org.redisson.api.RTopic;
import org.redisson.api.RedissonClient;
import org.redisson.api.listener.MessageListener;
import org.springframework.aop.framework.AopProxyUtils;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;

/**
 * @ClassName DCCValueBeanFactory
 * @Description 基于Redis 实现动态配置中心
 * @date 2025/7/26 20:55
 * @Version 1.0
 */
@Slf4j
@Configuration
//通过实现spring接口拿到Bean对象，然后拿到原对象，再拿到属性的字段，判断字段有没有加DCCValue注解
//如果有添加，就被我们管理，再判断一下有没有对应的值，如果没有配就抛异常
//如果有 就去截取里面的 key和默认值。默认值不能为空，为空抛异常
//然后再进行redis操作
public class DCCValueBeanFactory implements BeanPostProcessor {

    private  static final String BASE_CONFIG_PATH="group_buy_market_doc_";

    //用于记录获取的Bean对象，方便后续操作
    private final Map<String, Object> dccObject = new HashMap<>();

    //注入redission
    private final RedissonClient redissonClient;

    public DCCValueBeanFactory(RedissonClient redissonClient) {
        // 依赖通过构造器强制传入,
        this.redissonClient = redissonClient;
    }


    //2、redis中发布订阅的处理，有点像MQ
    @Bean("dccTopic")
    public RTopic dccRedisTopicListener(){
        RTopic topic = redissonClient.getTopic("group_buy_market_doc");

        //添加监听
        topic.addListener(String.class, new MessageListener<String>() { //new一个监听消息的操作
            @Override
            public void onMessage(CharSequence charSequence, String s) {
                //s的值 就是往出推送消息的过程中监听到的值 拆分一下 拿到信息
                String[] split = s.split(Constants.SPLIT);
                String attribute = split[0];

                String key=BASE_CONFIG_PATH+attribute;
                String value=split[1];

                RBucket<Object> bucket = redissonClient.getBucket(key);
                //判断是否存在
                boolean exists = bucket.isExists();
                if (!exists) return;//不存在直接返回

                bucket.set(value);
                Object objBean = dccObject.get(key);//获取对象
                if (objBean == null) return;

                //用对象 获取class
                Class<?> objBeanClass = objBean.getClass();
                if(AopUtils.isAopProxy(objBean)){
                    objBeanClass=AopUtils.getTargetClass(objBean);
                }

                try {
                    //反射填充field字段
                    Field field = objBeanClass.getDeclaredField(attribute);
                    field.setAccessible(true);//属性先变成可访问的状态
                    field.set(objBean,value);
                    field.setAccessible(false);
                }catch (Exception e){
                    throw new RuntimeException("......");
                }


            }
        });
        return topic;
    }





    //1、判断那些Bean对象下的属性 配置了DCC注解
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        //避免类是代理类，怕拿不到原来的类，增加一个判断
        Class<?> targetBeanClass = bean.getClass();
        Object targetBeanObject = bean;

        if (AopUtils.isAopProxy(bean)){//判断bean是否是AOP切面的代理类
            targetBeanClass=AopUtils.getTargetClass(bean);
            targetBeanObject=AopProxyUtils.getSingletonTarget(bean);
        }

        //拿到原来的类里面配置的属性
        Field[] fields = targetBeanClass.getDeclaredFields();
        for (Field field : fields) {
            if (!field.isAnnotationPresent(DCCValue.class)){//判断这个属性是否有这个注解
                continue;
            }
            //有就取出来
            DCCValue dccValue = field.getAnnotation(DCCValue.class);
            //拿到有这个自定义注解的这个属性的值
            String value = dccValue.value();
            if (StringUtils.isBlank(value)){//这个值为空的话抛出异常
                throw new RuntimeException("......");
            }

            //不为空 则去拆分 获得对应的key和值
            String[] splits = value.split(":");
            //拼接出来用过key
            String key = BASE_CONFIG_PATH.concat(splits[0]);
            String defaultValue = splits.length==2?splits[1]:null;

            String setValue=defaultValue;

            try {
                if (StringUtils.isBlank(setValue)){
                    throw new RuntimeException("......");
                }

                RBucket<String> bucket = redissonClient.getBucket(key);
                //判断redis中存不存在这个key 首次肯定不存在 不存在 我们插入 存在 我们get
                boolean exist = bucket.isExists();
                if (!exist){
                    bucket.set(value);
                }else{
                    setValue = bucket.get();
                }

                //反射填充field字段
                field.setAccessible(true);//属性先变成可访问的状态
                field.set(targetBeanObject,setValue);
                field.setAccessible(false);
            }catch (Exception e){
                throw new RuntimeException("......");
            }

            dccObject.put(key, targetBeanObject);





        }

        return bean;//返回bean对象
    }

    //3、需要去控制推送消息 在api层定义接口 ，然后在trigger中通过controller中实现
}
